/**
 * 
 */
package client;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.DatagramSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;

/**
 * @author Fleischhacker
 *
 */
public class Client {
	
	/**
	 * Client address
	 */
	private String address;
	/**
	 * TCP-Port used for reading mandidate server-send messages
	 */
	private int pTCP;
	/**
	 * UDP-Port used for reading Server-send messages
	 */
	
	private int pUDP;
	
	/**
	 * Socket used for Interacting with the Server
	 */
	private Socket sock;
	
	/**
	 * Outputstream
	 */
	private BufferedWriter out;
	
	/**
	 * Set false when client (or Server) is faced with shutdown commands
	 */
	private boolean alive;
	
	/**
	 * Thread used for receiving Server-send messages (UDP and TCP, latter for important commands like shutdown)
	 */
	private ClientTCPThread tcp = null;
	private ClientUDPThread udp = null;
	
	private ClientInputReader cir = null;
	/**
	 * Username after login
	 */
	private String client_username = null;

	/**
	 * Client - Implements UDP- and TCP-Listener
	 * Userinput (static)
	 * @param _address
	 * @param _pTCP
	 * @param _pUDP
	 */
	public Client(String _address, int _pTCP, int _pUDP){
		this.address = _address;
		this.pTCP = _pTCP;
		this.pUDP = _pUDP;
		alive = true;
	}
	
	/**
	 * Builds up the connection with the Server
	 * Has to be executed before the first contact with the server!!
	 */
	public void startUp(){
		if(alive && sock == null){
			try {
				sock = new Socket(address,pTCP);
				tcp = new ClientTCPThread(sock,this);
				udp = new ClientUDPThread(sock.getInetAddress(),pUDP,this);
				out = new BufferedWriter(new OutputStreamWriter(sock.getOutputStream()));
			} catch (UnknownHostException e) {
				System.out.println("= Unknown Host! Check Input!");
			} catch (IOException e) {
				System.out.println("ERROR @Client.login():"+e.getMessage());
			}
			tcp.start();
			udp.start();
		}
	}
	
	/**
	 * StartUp, sends important Information like UDP Port to the Server
	 * Invoked with !login <user>
	 */
	//public void login(String user){
		/*try {
			sock = new Socket(address,pTCP);
			tcp = new ClientTCPThread(sock,this);
			udp = new ClientUDPThread(sock.getInetAddress(),pUDP);
		} catch (UnknownHostException e) {
			System.out.println("= Unknown Host! Check Input!");
		} catch (IOException e) {
			System.out.println("ERROR @Client.login():"+e.getMessage());
		}
		tcp.start();
		udp.start();
		try{
			out = new BufferedWriter(new OutputStreamWriter(sock.getOutputStream()));
			out.write("!login "+user+" "+pUDP); //Pattern like !login <username> <UDPPORTNR>
			out.newLine();
			out.flush();
		}catch (IOException e) {System.out.println("ERROR @Client.startUp():"+e.getMessage());} 
		/*
		//Sending UDP Port to the Server
		BufferedWriter out = null;
		try {
			sock = new Socket(address,pTCP);
			out = new BufferedWriter(new OutputStreamWriter(sock.getOutputStream()));
			out.write("!login "+user+" "+pUDP); //Pattern like !login <username> <UDPPORTNR>
			out.newLine();
			out.flush();
		} catch (IOException e) {System.out.println("ERROR @Client.startUp():"+e.getMessage());} 
		finally{
			if(out != null){ //Closing outputstream
				try {
					out.close();
				} catch (IOException e) {System.out.println("ERROR @Client.startUp()/finally:"+e.getMessage());}//SHOULDN'T happen, ffs it shoudln't
			}
		}*/
		//Now starts the TCP Listener
		//Now starts the UDP Listener
		//ct = new ClientUDPThread(pUDP,this);
		//ct.start();
	//}
	
	/**
	 * Sends the command to the Server
	 * Checked by Client
	 * @param _cmd
	 */
	public void doCommands(String _cmd) throws Exception{
		startUp();
		try{ 
			//Sending the (already from client checked) commands to the Server
			if(_cmd.startsWith("!login"))
				_cmd += " "+pUDP;
			out.write(_cmd);
			out.newLine();
			out.flush();
		}catch(SocketException e){ //If the Server decided to shutdown
			throw new Exception("servershutdown");
		}catch(IOException ioe){
			ioe.printStackTrace();
			if(sock.isClosed()){
				try {
					out.close();
				} catch (IOException e) {e.printStackTrace();}
				sock = null;
			}
		}
	}
	
	/**
	 * Shutdown Client
	 * 	 Shutdown Order:
	 * 		1. Shutdown ClientThreads (and close those streams)
	 * 		2. Close Client Streams
	 * 		3. Shutdown Client (jump out of main per commandsToClient's end of life)
	 */
	public void shutdown(){
		alive = false;
		cir.shutdown();
		if(tcp != null && tcp.isAlive())
			tcp.shutdown();
		if(udp != null && udp.isAlive())
			udp.shutdown();
		try {
			if(sock != null){
				sock.close();
				sock = null;
			}
		} catch (IOException e) {
			System.out.println("ERROR @Client.shutdown():"+e.getMessage());
		}
	}
	
	/**
	 * Resock Client if the first attempt to connect was not login!
	 */
	public void resock(){
		try {
			if(sock != null){
				sock.close();
				sock = null;
			}
		} catch (IOException e) {
			System.out.println("ERROR @Client.shutdown():"+e.getMessage());
		}
	}
	
	/**
	 * Returns the connection-state
	 * @return	true - connected
	 * 			false	otherwise
	 */
	public boolean isConnected(){
		if(sock != null)
			return sock.isBound();
		else
			return false;
	}
	
	public boolean isAlive(){
		return alive;
	}
	
	public void setClientUser(String _user){
		this.client_username = _user;
	}
	public String getClientUser(){
		return this.client_username;
	}

	/**
	 * main
	 * @param args
	 */
	public static void main(String[] args) {
		String[] vals = checkAndParse(args);
		if(vals != null){
			System.out.println("============================================================");
			System.out.println("= Client starting!                                         =");
			System.out.println("= Client connecting to ["+vals[0]+"]");
			System.out.println("= Servers TCP Port: ["+vals[1]+"]                                =");
			System.out.println("= Clients UDP Port: ["+vals[2]+"]                                =");
			Client c;
			c = new Client(vals[0],Integer.parseInt(vals[1]),Integer.parseInt(vals[2]));
			System.out.println("= Available commands:                                      =");
			System.out.println("=   !login <username[STRING]>                              =");
			System.out.println("=   !logout                                                =");
			System.out.println("=   !list                                                  =");
			System.out.println("=   !create <duration[INT]secs> <description[STRING]>      =");
			System.out.println("=   !bid <auction-id[INT]> <amount[NUMBER.2]>              =");
			System.out.println("=   !end                                                   =");
			System.out.print("= >");
			c.runCommandReader();
			//System.out.println("= COMMAND PROMPT TERMINATED!                               =");
		}
	}
	
	/**
	 * Checks the Params of the main
	 * 		Awaited Parameters: STRING NUMBER NUMBER
	 * 		Awaited Syntax: java client.Client STRING NUMBER NUMBER
	 * @param args - given Params
	 * @return -1 if the given Argument(s) is no Number
	 * 			Portnumber otherwise
	 */
	@SuppressWarnings("finally")
	private static String[] checkAndParse(String[] args){
		String[] ret = null;
		try{
			if(args.length == 3){
				ret = new String[3];
				ret[0] = args[0];
				ret[1] = Integer.parseInt(args[1])+"";
				ret[2] = Integer.parseInt(args[2])+"";
			}
			else
				throw new NumberFormatException();//Abusing Exception for less Code
		}catch(NumberFormatException nfe){
			System.out.println("Argument error!\nSyntax: java client.Client STRING[1] NUMBER[2] NUMBER[3]\nParameter Information:\n + [1] Server Address\n + [2] Server Portnumber\n + [3] Own UDP Portnumber");
		}catch(Exception e){
			System.out.println("ERROR @MAIN.checkAndParse(String[]):"+e.getMessage());
		}
		finally{
			return ret;
		}
	}
	
	public void runCommandReader(){
		cir = new ClientInputReader(this);
		cir.start();
	}
	
	/**
	 * Reads and checks the commands
	 * @return True cuz it can
	 */
	/*private boolean commandsToClient(){
		BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
		String read = null;
		try{
			read = in.readLine();
			if(read.startsWith("!login")){
				if(read.split(" ").length != 2)
					throw new Exception("userfailure"); //Abusing Exception System; Wrong Input => Shutdown
				this.doCommands(read);
			}
			else if(read.startsWith("!logout")){
				if(read.split(" ").length != 1)
					throw new Exception("userfailure"); //Abusing Exception System; Wrong Input => Shutdown
				this.doCommands(read);
			}
			else if(read.startsWith("!list")){
				if(read.split(" ").length != 1)
					throw new Exception("userfailure"); //Abusing Exception System; Wrong Input => Shutdown	
				this.doCommands(read);
			}
			else if(read.startsWith("!create")){
				if(read.split(" ").length <= 2)
					throw new Exception("userfailure"); //Abusing Exception System; Wrong Input => Shutdown
				else
					Integer.parseInt(read.split(" ")[1]); //Throws NumberFormatException if the user screws up
				this.doCommands(read);
			}
			else if(read.startsWith("!bid")){
				if(read.split(" ").length != 3)
					throw new Exception("userfailure"); //Abusing Exception System; Wrong Input => Shutdown
				else{
					Double.parseDouble(read.split(" ")[1]); //Throws NumberFormatException if the user screws up
					Double.parseDouble(read.split(" ")[2]);
				}
				this.doCommands(read);
			}
			else if(read.startsWith("!end")){	//End of the line, force Shutdown on both Client and ClientThreads
				//TODO: Shutdown Client
				this.doCommands(read);
				this.alive = false;
				//client.shutdown(); //not needed
				return true;
			}
			else if(read.length() == 0){} //Empty Text is NO invalid command (assumption)
			else{ //Woops, invalid command. Shutdown and go!
				throw new Exception("userfailure"); //Abusing Exception System again;
			}
		}catch(IOException ioe){
			System.out.println("ERROR @MAIN.commandsToClient(Client):"+ioe.getMessage());
		}catch(NumberFormatException nfe){
			if(this.isAlive()) //Output Tweak
				System.out.println("= Please use the according format!");
			this.shutdown();
		}catch(Exception e){
			//e.printStackTrace();
			if(e != null && e.getMessage().equalsIgnoreCase("userfailure")){ //Exception Abuse;
				if(this.isAlive()) //Output Tweak
					System.out.println("= Invalid Command!\n= Please use the given commands with their syntax!\n= Shutting down Client...");
				this.shutdown();
			}
			else if(e != null && e.getMessage().equalsIgnoreCase("servershutdown")){ //Exception Abuse; Server shutdown
				System.out.println("= Server went offline!");
			}else
				System.out.println("ERROR @MAIN.commandsToClient(Client):"+e.getMessage());
		}
		finally{
			return true;
		}
	}*/
}
